package com.lfz.demo.service.Aspect;

import cn.hutool.core.thread.ThreadUtil;
import com.lfz.demo.common.target.HoneyLogs;
import com.lfz.demo.controller.LogsController;
import com.lfz.demo.entity.*;
import com.lfz.demo.service.LogsService;
import com.lfz.demo.service.UserService;
import com.lfz.demo.utils.DateUtils;
import com.lfz.demo.utils.IPUtils;
import com.lfz.demo.utils.RedisUtil;
import com.lfz.demo.vo.DataView;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.spel.ast.Operator;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * @author lfz
 * @date 2025/3/12 10:25
 */
@Aspect
@Component
public class LogsAspect {

    @Resource
    private LogsService logsService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisUtil redisUtil;

    private static final Logger logger = LoggerFactory.getLogger(LogsController.class);
    private static final String CACHE_PREFIX = "logs:";
    private static final int CACHE_EXPIRE_MINUTES = 30;

    /**
     * 后置通知，当切点执行完以后执行
     * 此方法属于后置通知，在带有 @HoneyLogs 注解的方法执行完毕后运行，其主要功能是记录操作日志。
     */
    @AfterReturning(pointcut = "@annotation(honeyLogs)", returning = "jsonResult")
    public Object recordLog(JoinPoint joinPoint, HoneyLogs honeyLogs, Object jsonResult) {
        try {
            // 获取IP信息
            String ipAddr = getIpAddr();

            // 获取方法参数并动态修改操作描述
            String operation = getOperationWithParams(joinPoint, honeyLogs, jsonResult);

            // 获取操作人信息
            String operator = getOperator(joinPoint);

            // 组装日志的实体对象
            Logs logs = buildLogEntity(honeyLogs, operator, ipAddr, operation);

            // 插入数据到数据库
            ThreadUtil.execAsync(() -> {
                try {
                    logsService.save(logs);
                } catch (Exception e) {
                    logger.error("保存日志信息时出现异常", e);
                }
            });
        } catch (Exception e) {
            logger.error("记录日志时出现异常", e);
        }
        return null;
    }

    /**
     * 此方法用于获取当前登录的用户信息（已弃用）
     * @return
     */
     /*private User getCurrentUser() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            HttpSession session = request.getSession();
            return (User) session.getAttribute("user");
        }
        return null;
    }
*/


    /**
     * 此方法用于获取用户的 IP 地址。
     * @return
     */
    private String getIpAddr() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            return IPUtils.getIpAddr(request);
        }
        return null;
    }

    /**
     * 创建组装日志的实体对象的方法
     * 功能
     * 此方法的作用是根据传入的参数构建一个 Logs 对象，该对象包含了操作日志的详细信息。
     * 参数
     * honeyLogs：HoneyLogs 注解的实例，包含操作菜单、操作类型和操作详情等信息。
     * currentUser：当前登录的用户对象，用于获取操作人的用户名。
     * ipAddr：操作人的 IP 地址。
     * operation：动态修改后的操作描述。
     */
    private Logs buildLogEntity(HoneyLogs honeyLogs, String operator, String ipAddr, String operation) {
        return Logs.builder()
                .operateTime(DateUtils.getNowDate())            // 操作时间
                .operateMenu(honeyLogs.menu().getValue())       // 操作菜单
                .ip(ipAddr)                                     // 操作人ip
                .operator(operator)                             // 操作人
                .operateType(honeyLogs.type().getValue())       // 操作类型
                .operateDetail(operation)                       // 操作详情
                .build();
    }

    /**
     * 功能
     * 该方法根据方法名和方法参数动态修改操作描述，使日志信息更加详细。
     * 参数
     * @param joinPoint JoinPoint 对象，包含了方法的签名和参数信息。
     * @param honeyLogs HoneyLogs 注解的实例，包含操作详情。
     * @param jsonResult
     * @return
     */
    private String getOperationWithParams(JoinPoint joinPoint, HoneyLogs honeyLogs, Object jsonResult) {
        String operation = honeyLogs.operation();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        User user;
        Integer id;


        switch (method.getName()) {
            // 删除类型操作日志
            case "deleteById":
                id = (Integer) args[0];
                operation = String.format("%s数据，ID: %d", operation, id);
                break;

            // 批量删除类型操作日志
            case "batchDelete":
                List<Integer> ids = (List<Integer>) args[0];
                operation = String.format("%s数据，ID列表: %s", operation, ids);
                break;

            // 中国人口管理相关操作日志
            case "addOrUpdateChina":
                ChinaPopulation popData = (ChinaPopulation) args[0];
                Date updateTime = popData.getUpdateTime();
                String strUpdateTime = DateUtils.formatDate(updateTime, DateUtils.DATE_YYYY_MM_DD);
                operation = String.format(
                        "%s数据，ID: %d，城市名称: %s，常住人口: %d 万，出生人口: %d 万，死亡人口: %d 万，" +
                        "性别比: %s，" + "普查时间: %s, 统计时间: %s", operation, popData.getId(), popData.getCityName(),
                        popData.getPopulation(), popData.getBirthNum(), popData.getDeadNum(), popData.getSexRatio(),
                        popData.getCensusTime().getDesc(), strUpdateTime
                );
                break;
            case "excelImportChina":
                MultipartFile file = (MultipartFile) args[0];
                operation = String.format(
                        "%s数据，上传的文件名为: %s",
                        operation, file.getOriginalFilename()
                );
                break;
            case "excelOutPutChina":
                String exportedFileName = (String) args[0]; // 直接从参数获取文件名
                operation = String.format("%s数据，导出的文件名为: %s", operation, exportedFileName);
                break;

            // 新闻相关操作日志
            case "addNews":
            case "updateNews":
                PopulationNews news = (PopulationNews) args[0];
                operation = String.format(
                        "%s数据，ID: %d，标题: %s，内容：%s，发布人：%s",
                        operation, news.getId(), news.getTitle(), news.getContent(), news.getPubulishby()
                );
                break;
            // 公告/通知相关操作日志
            case "addNotice":
            case "updateNotice":
            case "publishNotice":
            case "closeNotice":
                Notice notice = (Notice) args[0];
                // 直接使用已获取的 operator 变量，无需从参数获取 User
                operation = String.format(
                        "%s公告/通知，ID: %d，标题: %s，内容：%s，操作人：%s",
                        operation, notice.getId(), notice.getTitle(), notice.getContent(), getOperator(joinPoint)
                );
                break;
            // 用户管理相关操作日志
            case "addUser":
            case "updateUser":
            case "changePassword":
                user = (User) args[0];
                operation = String.format(
                    "%s操作，用户名：%s，密码：%s",
                    operation, user.getUsername(), user.getPassword()
                );
                break;
            case "resetPwd":
                id = (Integer) args[0];
                // 通过id获取用户
                user = userService.getById(id);
                operation = String.format(
                        "%s操作，用户名：%s，重置密码：%s",
                        operation, user.getUsername(), user.getPassword()
                );

                // 头像相关操作日志
            case "uploadByUser":
                user = (User) args[1];
                operation = String.format(
                        "%s操作，用户名：%s，头像路径：%s",
                        operation, user.getUsername(), user.getImgPath()
                );
                break;
            case "uploadByUid":
                Long userId = (Long) args[1];                  // 第二个参数是 userId
                String username = "未知用户";

                // 根据 userId 查询用户信息
                user = userService.getById(userId);
                if (user != null) {
                    username = user.getUsername();
                }

                operation = String.format(
                        "%s操作，用户ID: %d，用户名: %s，头像路径: %s",
                        operation, userId, username, user.getImgPath()
                );
                break;

            // 登录操作日志
            case "login":
                username = (String) args[0];
                String password = (String) args[1];
                String code = (String) args[2];
                DataView loginResult = (DataView) jsonResult;
                operation = String.format(
                        "%s操作，用户名: %s，密码: %s，验证码: %s，结果: %s",
                        operation, username, password, code, loginResult.getMsg()
                );
                break;

            // 注册操作日志
            case "register":
                user = (User) args[0];
                DataView registerResult = (DataView) jsonResult;
                operation = String.format(
                        "%s操作，用户名: %s，结果: %s",
                        operation, user.getUsername(), registerResult.getMsg()
                );
                break;
        }
        clearRelatedCaches();
        return operation;
    }

    /**
     * 获取操作人信息
     * @param joinPoint
     * @return
     */
    private String getOperator(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();

        if ("register".equals(method.getName())) {
            User user = (User) args[0];
            return user.getUsername();
        } else {
            // 尝试从会话中获取登录用户信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                Object userObj = request.getSession().getAttribute("user");
                if (userObj instanceof User) {
                    User user = (User) userObj;
                    return user.getUsername();
                }
            }
        }
        return "system";
    }

    /**
     * 清理相关缓存
     */
    private void clearRelatedCaches() {
        try {
            redisUtil.deletePattern(CACHE_PREFIX + "*");
//            logger.info("成功清理日志相关缓存");
        } catch (Exception e) {
            logger.error("缓存清理失败", e);
        }
    }

}
